/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package lolbatfsm.implementation.textui;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import lolbatfsm.implementation.io.FSMFileStore;
import lolbatfsm.io.FSMStore;
import lolbatfsm.io.SimulationController;
import lolbatfsm.io.SimulationListener;
import lolbatfsm.system.AsynchronousTransition;
import lolbatfsm.system.EquivalenceClass;
import lolbatfsm.system.FSM;
import lolbatfsm.system.Simulator;
import lolbatfsm.system.State;
import lolbatfsm.system.SynchronousTransition;
import lolbatfsm.system.Transition;

/**
 *
 * @author Matteo Battaglio <matteo.battaglio@gmail.com>
 */
@Deprecated
public class TextUI implements SimulationController, SimulationListener {

    private static final int USER_CHOICE_LOAD = 1;
    private static final int USER_CHOICE_CREATE = USER_CHOICE_LOAD + 1;

    private final Simulator simulator = Simulator.getInstance();
    private BufferedReader reader;
    private FSMStore fsmStore;
    private final Set<FSM> fsmSet = new HashSet<FSM>();
    private final Set<EquivalenceClass> equivalenceClassSet =
            new HashSet<EquivalenceClass>();
    private int transitionIndex;

    public TextUI() {
        simulator.setSimulationController(this);
        simulator.addSimulationListener(this);
        reader = new BufferedReader(new InputStreamReader(System.in));
        fsmStore = new FSMFileStore();
        fsmSet.addAll(fsmStore.loadFSMs());
        System.out.println("Available FSMs:");
        boolean answer;
        do {
            printFSMList();
            answer = askYesOrNoQuestion("Do you want to create a new FSM?");
            if (answer) {
                FSM newFsm = createFSM();
                if (newFsm != null) {
                    fsmSet.add(newFsm);
                }
            }
        } while (answer);
        fsmStore.storeFSMs(fsmSet);
    }

    @Override
    public Integer[][] selectTransitions(ArrayList<AsynchronousTransition> selectableAsyncTransitions,
            ArrayList<? extends Collection<SynchronousTransition>> selectableSyncTransitions,
            boolean allowNullSelection) {
        
        Integer[][] selectedTransitionIndexes = new  Integer[2][];
        Set<Integer> selectedTransitions = new TreeSet<Integer>();
        Set<Integer> selectableTransitions = new TreeSet<Integer>();
        int asyncTransitionsCount = selectableAsyncTransitions.size();
        int syncTransitionsCount = selectableSyncTransitions.size();
        int totalCount = asyncTransitionsCount + syncTransitionsCount;
        for (int i = 0; i < totalCount; i++) {
            selectableTransitions.add(i+1);
        }

        boolean terminate = false;
        do {
            System.out.println("Select the next transition to trigger, or q to quit the selection.");
            System.out.println("Asynchronous transitions:");
            for (int i = 0; i < asyncTransitionsCount; i++) {
                if (selectableTransitions.contains(i + 1)) {
                    AsynchronousTransition t = selectableAsyncTransitions.get(i);
                    System.out.printf("%d) %s\n", i + 1, t);
                }
            }
            System.out.println("Reciprocally-synchronous transitions:");
            for (int i = 0; i < syncTransitionsCount; i++) {
                if (selectableTransitions.contains(asyncTransitionsCount + i)) {
                    Collection<SynchronousTransition> recSyncTransitions = selectableSyncTransitions.get(i);
                    System.out.printf("%d)", asyncTransitionsCount + i);
                    for (SynchronousTransition t : recSyncTransitions) {
                        System.out.printf("\t%s,\n", t);
                    }
                }
            }

            String selection = null;
            boolean invalidSelection;
            do {
                invalidSelection = false;
                System.out.println("\nSelection:");
                try {
                    selection = reader.readLine();
                    System.out.println();
                    if (selection.equalsIgnoreCase("q")) {
                        if (selectedTransitions.isEmpty() && !allowNullSelection) {
                            System.out.println("You must select at least 1 transition to trigger.");
                            invalidSelection = true;
                        } else {
                            terminate = true;
                        }
                    } else {
                        int selectionIndex = Integer.parseInt(selection);
                        if (selectableTransitions.remove(selectionIndex)) {
                            selectedTransitions.add(selectionIndex);
                            terminate = selectableTransitions.isEmpty();
                        } else {
                            System.out.printf("Invalid selection index: the index must" +
                                    " be in the range [1,%d]\n", totalCount);
                            invalidSelection = true;
                        }
                    }
                } catch (NumberFormatException ex) {
                    invalidSelection = true;
                    System.out.println("The selection must be a valid integer number. Retry.");
                } catch (IOException ex) {
                    System.out.println("I/O Error. Retry.");
    //                    ex.printStackTrace();
                }
            }
            while (invalidSelection);
        }
        while (!terminate);
        
        return selectedTransitionIndexes;
    }

//    @Override
//    public Map<FSM, Transition> selectTransitions(Collection<FSM> selectableFSMs,
//            boolean allowNullSelection) {
//        Map<FSM, Transition> selectedTransitions = new HashMap<FSM, Transition>();
//
//        boolean terminate = false;
//        do {
//            int count = 0;
//            Map<EquivalenceClass, List<SynchronousTransition>> syncTransMap = new HashMap<EquivalenceClass, List<SynchronousTransition>>();
//            Map<Integer, Transition> selectionIndexMap = new TreeMap<Integer, Transition>();
//
//            System.out.println("Select the next transition to trigger, or q to quit the selection.");
//            for (FSM fsm : selectableFSMs) {
//                System.out.printf("FSM \"%s\"'s enabled transitions:\n", fsm);
//                for (Transition t : fsm.getEnabledTransitions()) {
//                    if (t instanceof SynchronousTransition) {
//                        syncTransMap.get
//                    }
//                    selectionIndexMap.put(++count, t);
//                    System.out.printf("%d) %s\n", count, t);
//                }
//            }
//
//            String selection = null;
//            boolean invalidSelection;
//            do {
//                invalidSelection = false;
//                System.out.println("\nSelection:");
//                try {
//                    selection = reader.readLine();
//                    System.out.println();
//                    if (selection.equalsIgnoreCase("q")) {
//                        if (selectedTransitions.isEmpty() && !allowNullSelection) {
//                            System.out.println("You must select at least 1 transition to trigger.");
//                            invalidSelection = true;
//                        } else {
//                            terminate = true;
//                        }
//                    } else {
//                        int selectionIndex = Integer.parseInt(selection);
//                        if (selectionIndexMap.containsKey(selectionIndex)) {
//                            Transition t = selectionIndexMap.remove(selectionIndex);
//                            selectableFSMs.remove(t.getOwnerFSM());
//                            selectedTransitions.put(t.getOwnerFSM(), t);
//                            terminate = selectableFSMs.isEmpty();
//                        } else {
//                            System.out.printf("Invalid selection index: the index must" +
//                                    " be in the range [1,%d]\n", count);
//                            invalidSelection = true;
//                        }
//                    }
//                } catch (NumberFormatException ex) {
//                    invalidSelection = true;
//                    System.out.println("The selection must be a valid integer number. Retry.");
//                } catch (IOException ex) {
//                    System.out.println("I/O Error. Retry.");
////                    ex.printStackTrace();
//                }
//            }
//            while (invalidSelection);
//        }
//        while (!terminate);
//        return selectedTransitions;
//    }

    @Override
    public void errorOccurred(String string) {
        System.out.println("ERROR: "+string);
    }

    @Override
    public void iterationCompleted(int iterationDuration, Collection<FSM> fsms) {
        System.out.println("Iteration completed:");
        System.out.println("  duration: " + iterationDuration);
        for (FSM fsm : fsms) {
            System.out.printf("  FSM \"%s\"'s current state: %s", fsm, fsm.getCurrentState());
            if (fsm.hasPendingTransition()) {
                System.out.printf(" - transition pending: %d seconds remaining",
                        fsm.getPendingTransition().getResidualDuration());
            }
            System.out.println();
        }
        System.out.println("Press Enter to continue...");
        try {
            reader.readLine();
        } catch (IOException ex) {
            System.out.println("I/O Error occurred.");
//            ex.printStackTrace();
        }
    }

    @Override
    public void simulationEnded() {
        System.out.println("Simulation ended.");
    }

    private boolean askYesOrNoQuestion(String question) {
        boolean answer = false, invalidSelection;
        
        do {
            invalidSelection = false;
            try {
                System.out.print(question + " [Y/n]: ");
                String selection = reader.readLine();
                if (selection.equals("")) {
                    selection = "Y";
                }
                if (!(selection.equalsIgnoreCase("Y") || selection.equalsIgnoreCase("N"))) {
                    invalidSelection = true;
                    System.out.println("Invalid choice. Retry.");
                }
                answer = selection.equalsIgnoreCase("Y") ? true : false;
            } catch (NumberFormatException ex) {
                invalidSelection = true;
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        } while (invalidSelection);
        return answer;
    }

    private FSM createFSM() {
        try {
            List<State> states = new LinkedList<State>();
            List<Transition> transitions = new LinkedList<Transition>();
            System.out.println("Enter the name for the new FSM:");
            String fsmName = reader.readLine();
            System.out.println("Enter the name of the initial state of the FSM:");
            String initialStateName = reader.readLine();
            State initialState = new State(initialStateName);
            states.add(initialState);
            boolean terminate = false;
            boolean invalidSelection = false;
            do {
                State sourceState = null, destState = null;
                for (int i = 0; i < 2; i++) {
                    String sourceOrDestination = (i == 0) ? "source" : "destination";
                    System.out.println("Select the " + sourceOrDestination +
                            " state for the next transition, or type q to terminate:");

                    int index = 0, stateCount = states.size();
                    for (State state : states) {
                        System.out.printf("%d) %s\n", ++index, state.getName());
                    }

                    if (i == 1) {
                        System.out.printf("%d) New State\n", index + 1);
                    }

                    do {
                        invalidSelection = false;
                        try {
                            String selection = reader.readLine();
                            System.out.println();
                            if (selection.equalsIgnoreCase("q")) {
                                terminate = true;
                            } else {
                                int selectionIndex = Integer.parseInt(selection);
                                if (1 <= selectionIndex && selectionIndex <= (stateCount + i)) {
                                    if (i == 0) {
                                        sourceState = states.get(selectionIndex - 1);
                                    } else {
                                        if (selectionIndex == stateCount + 1) {
                                            System.out.println("Enter the name of the new" +
                                                    " state of the FSM:");
                                            String newStateName = reader.readLine();
                                            destState = new State(newStateName);
                                            states.add(destState);
                                            stateCount++;
                                        } else {
                                            destState = states.get(selectionIndex - 1);
                                        }
                                    }
                                } else {
                                    System.out.printf("Invalid selection index: the index must" +
                                            " be in the range [1,%d]\n", stateCount + i);
                                    invalidSelection = true;
                                }
                            }
                        } catch (NumberFormatException ex) {
                            invalidSelection = true;
                            System.out.println("The selection must be a valid integer number. Retry.");
                        }
                    } while (invalidSelection && !terminate);

                    if (terminate) {
                        break;
                    }
                }

                if (sourceState != null && destState != null) {
                    int duration = 0;
                    do {
                        System.out.println("Enter the duration of the transition:");
                        String durationStr = reader.readLine();
                        try {
                            duration = Integer.parseInt(durationStr);
                            if (duration <= 0) {
                                invalidSelection = true;
                                System.out.println("Error: the duration of the transition must be > 0");
                            }
                        } catch (NumberFormatException ex) {
                            invalidSelection = true;
                            System.out.println("The selection must be a valid integer number. Retry.");
                        }
                    } while (invalidSelection);
                    Transition transition = new AsynchronousTransition(sourceState, destState, duration);
                    transitions.add(transition);
                }
            } while (!terminate);

            FSM fsm = new FSM(fsmName, initialState, transitions.toArray(new Transition[transitions.size()]));
            return fsm;
        } catch (IOException ex) {
            Logger.getLogger(TextUI.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    private void printFSMList() {
        int count = 0;
        for (FSM fsm : fsmSet) {
            System.out.printf("%d) %s\n", ++count, fsm.getName());
        }
    }

    public void startSimulation() {
        simulator.startSimulation(fsmSet, equivalenceClassSet);
    }

}
